import {
  MeasureResultType,
  ParameterObjectType,
  RawMeasureResult,
  useUnit,
} from '@amcax/base';
import { cmdMgr } from '@app-base/manager/CmdManager';
import { dataManager, unitManager } from '@app-base/module';
import { floor, round } from 'lodash';
import { useMemo } from 'react';
import { Vector3Tuple } from 'three-legacy';
import { MeasureAngle } from './Angle';
import { MeasureDistanceObj } from './Distance';
import { MeasureLabel } from './Label';
import {
  MeasureLabelPositions,
  MeasureNumberResultWithLabel,
  MeasureRenders,
  MeasureResult,
} from './types';

export const parseMeasureResult = (
  fullResult: RawMeasureResult,
  id?: string,
  labelPosition?: MeasureLabelPositions,
) => {
  const result = fullResult.result;

  return {
    id: id ?? fullResult.id,
    dxid1: fullResult.dxid1,
    dxid2: fullResult.dxid2,
    length:
      result.length == null
        ? result.length
        : {
            ...result.length,
            type: MeasureResultType.Length,
            labelPosition: labelPosition?.length,
          },
    radius:
      result.radius == null
        ? null
        : {
            ...result.radius,
            type: MeasureResultType.Radius,
            labelPosition: labelPosition?.radius,
          },
    minDistance:
      result.minDistance == null
        ? null
        : {
            ...result.minDistance,
            type: MeasureResultType.MinDistance,
            labelPosition: labelPosition?.minDistance?.full,
            labelPositionX: labelPosition?.minDistance?.x,
            labelPositionY: labelPosition?.minDistance?.y,
            labelPositionZ: labelPosition?.minDistance?.z,
          },
    centerDistance:
      result.centerDistance == null
        ? null
        : {
            ...result.centerDistance,
            type: MeasureResultType.CenterDistance,
            labelPosition: labelPosition?.centerDistance?.full,
            labelPositionX: labelPosition?.centerDistance?.x,
            labelPositionY: labelPosition?.centerDistance?.y,
            labelPositionZ: labelPosition?.centerDistance?.z,
          },
    angle:
      result.angle == null
        ? null
        : {
            ...result.angle,
            type: MeasureResultType.Angle,
            labelPosition: labelPosition?.angle,
          },
    area:
      result.area == null
        ? null
        : {
            value: result.area,
            geometry: result.geometry,
            labelPosition: labelPosition?.area,
          },
    round:
      result.round == null
        ? null
        : {
            value: result.round,
            geometry: result.geometry,
            labelPosition: labelPosition?.round,
          },
    coordinate: result.coordinate,
  } as MeasureResult;
};

export const renderMeasureResult = async (result: MeasureResult) => {
  await cmdMgr.fetchDependentShapeInfo([result.dxid1, result.dxid2]);

  const { minDistance, centerDistance, angle } = result;

  const renders: MeasureRenders = {};

  if (minDistance != null) {
    renders.minDistance = new MeasureDistanceObj(minDistance, 'min');
  }

  if (centerDistance != null) {
    renders.centerDistance = new MeasureDistanceObj(centerDistance, 'center');
  }

  const getNumberResult = (
    numberResult: MeasureNumberResultWithLabel,
    type: ParameterObjectType | 'Area',
    label: string,
  ) => {
    const position = numberResult.geometry.pt.some((v) => Math.abs(v) > 1e30)
      ? getFaceCenterByPickId(result.dxid1)
      : numberResult.geometry.pt;
    return new MeasureLabel({
      label,
      value: numberResult.value,
      type,
      position,
      pickTargetID: result.dxid1,
      labelPosition: numberResult.labelPosition,
    });
  };

  if (result.area != null) {
    renders.area = getNumberResult(result.area, 'Area', '面积');
  }

  if (result.round != null && result.area == null) {
    renders.round = getNumberResult(
      result.round,
      ParameterObjectType.Length,
      '周长',
    );
  }

  if (result.length != null) {
    renders.length = getNumberResult(
      result.length,
      ParameterObjectType.Length,
      '长度',
    );
  }

  if (angle != null) {
    renders.angle = new MeasureAngle(angle, [result.dxid1, result.dxid2]);
  }

  return renders;
};

export const getFaceCenterByPickId = (pickId: string) => {
  const regResult = /^(.*):f(\d+)$/.exec(pickId);

  if (regResult == null) {
    const { partKey } = dataManager.getObjectInfoByDxid(pickId) || {};
    const data = dataManager.getShapeData(pickId, partKey);

    return data?.faces?.[0]?.center as Vector3Tuple;
  }

  const dxid = regResult[1];
  const index = Number(regResult[2]);

  const { partKey } = dataManager.getObjectInfoByDxid(dxid) || {};
  const data = dataManager.getShapeData(dxid, partKey);

  return data?.faces?.[index]?.center as Vector3Tuple;
};

export const useMeasureText = (
  value: number,
  type: ParameterObjectType | 'Area',
  precision = 6,
) => {
  const unitConfig = useUnit();

  const getValueWithPrecision = (val: number) =>
    floor(val, precision) === 0 ? 0 : round(val, precision);
  const text = useMemo(() => {
    if (
      [ParameterObjectType.Real, ParameterObjectType.Integer].includes(
        type as ParameterObjectType,
      )
    ) {
      return getValueWithPrecision(value);
    }

    if (type === ParameterObjectType.Length) {
      const unit = unitManager.getUnitParams(unitConfig.lengthUnit);
      return `${getValueWithPrecision(value / unit.multiple)} ${unit.name}`;
    }

    if (type === ParameterObjectType.Angle) {
      const unit = unitManager.getUnitParams(unitConfig.angleUnit);
      return `${getValueWithPrecision(value / unit.multiple)} ${unit.name}`.replace(
        ' deg',
        '°',
      );
    }

    if (type === 'Area') {
      const unit = unitManager.getUnitParams(unitConfig.lengthUnit);
      return `${getValueWithPrecision(value / unit.multiple ** 2)} ${unit.name}²`;
    }

    return '';
  }, [value, type, unitConfig.lengthUnit, unitConfig.angleUnit]);

  return text;
};

export const getCSSColorByNumber = (color: number) =>
  color == null ? null : `#${color.toString(16).padStart(6, '0')}`;
